package me.chenqiang.cinc;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;

import me.chenqiang.cinc.passportutils.CountryHuffmanCodec;
import me.chenqiang.cinc.passportutils.PassportNumCodecs;
import me.chenqiang.cinc.transformers.CitizenIDCodec;
import me.chenqiang.cinc.transformers.FiveStartIDCodec;
import me.chenqiang.cinc.transformers.HMTTravellerIDCodec;
import me.chenqiang.cinc.transformers.LegacyPRIDCodec;

public class CincCoder {
	@FunctionalInterface
	public static interface Encoder {
		String compress(String numbers) throws CincCodingException;
	}
	protected static final List<Function<String, IDType>> VALIDATORS = List.of(
			CitizenIDCodec::validate, FiveStartIDCodec::validate, LegacyPRIDCodec::validate, HMTTravellerIDCodec::validate
			);
	protected static final Map<IDType, Encoder> ENCODERS = Map.ofEntries(
			Map.entry(IDType.CITIZEN_ID, CitizenIDCodec::compress),
			Map.entry(IDType.PERMENENT_RESIDENT_ID, FiveStartIDCodec::compress),
			Map.entry(IDType.LEGACY_PR_ID, LegacyPRIDCodec::compress),
			Map.entry(IDType.HONGKONG_MACAU_RESIDENT_TRAVELLER_ID, HMTTravellerIDCodec::compress),
			Map.entry(IDType.TAIWAN_RESIDENT_TRAVELLER_ID, HMTTravellerIDCodec::compress)
			);

	private CincCoder() {
	}
	
	public static String encode(String idn) throws CincCodingException {
		for(Function<String, IDType> validator: VALIDATORS) {
			IDType t = validator.apply(idn); 
			if(t != null) {
				return ENCODERS.get(t).compress(idn);
			}
		}
		return null;
	}
	
	public static Entry<IDType, String> decode(String encoded) throws CincCodingException {
		long numval = CincCommons.toNumbers(encoded);
		long prefix = numval >> 38;
		if(prefix > 0 && prefix < 0x60) {
			return Map.entry(IDType.CITIZEN_ID, CitizenIDCodec.decompress(encoded));
		}
		else if(prefix >= 0x64 && prefix < 0x67) {
			String num = HMTTravellerIDCodec.decompress(encoded);
			return Map.entry(HMTTravellerIDCodec.validate(num), HMTTravellerIDCodec.decompress(encoded));
		}
		else if(prefix >= 0x68 && prefix < 0x70) {
			return Map.entry(IDType.LEGACY_PR_ID, LegacyPRIDCodec.decompress(encoded));
		}
		else if(prefix >= 0x70) {
			return Map.entry(IDType.PERMENENT_RESIDENT_ID, FiveStartIDCodec.decompress(encoded));
		}
		else {
			return null;
		}
	}
	
	
	public static String encodePassport(String countryCode, String passportNum) throws CincCodingException {
		return CincCommons.toSymbols(PassportNumCodecs.hash(countryCode, passportNum), 9, '0');
	}

	public static String extractPassportIssuingCountry(String encoded) throws CincCodingException {
		long hash = CincCommons.toNumbers(encoded);
		StringBuilder sb = new StringBuilder();
		String bitstring = Long.toBinaryString(hash);
		for(int i = 0; i < 45 - bitstring.length(); i++) {
			sb.append('0');
		}
		sb.append(bitstring);
		
		return CountryHuffmanCodec.decode(sb.toString(), 1).get(0);	
	}
}
